package codingDudes.display;

import java.util.Vector;

import codingDudes.utils.MathUtils;

/**
 * The DisplayObjectContainer is an extension of the DisplayObject class.
 * 
 * It provides functionality for creating bundles of DisplayObjects and nesting in
 * any depth.
 * 
 * First the default drawing routines of the DisplayObject superclass will be executed.
 * Afterwards all DisplayObjects in the display list are rendered.
 * 
 * @author Tobias Friese
 */

public class DisplayObjectContainer extends DisplayObject
{
	/* variables */
	private Vector<DisplayObject> children = new Vector<DisplayObject>();
	private boolean childsRendered;
	
	/* rendering */
	
	/**
	 * Starts rendering process and checks for initialization and visibility first.
	 * Use this method to render the content of your DisplayObject if it is not child of any DisplyObjectContainer.
	 * 
	 * Own graphic content is rendered first, the appended children afterwards.
	 */
	public final void render()
	{
		this.childsRendered = false;
		
		super.render();
		if(this.visible) if(initialized) if(!this.finalized) if(!this.childsRendered) this.renderChilds();
	}
	
	/**
	 * Initiates child rendering. This method can be executed during the
	 * drawing routine to manage depth issues.
	 * 
	 * If not called manually it will be called automatically after the drawing routine.
	 */
	@SuppressWarnings("unchecked")
	protected final void renderChilds()
	{
		if(!this.childsRendered)
		{
			Vector<DisplayObject> childs = (Vector<DisplayObject>)this.children.clone();
			
			this.pushMatrix();
			for(int i = 0; i < childs.size(); i++) childs.get(i).render();
			this.popMatrix();
			
			this.childsRendered = true;
		}
	}
	
	/* adding and removing */
	
	/**
	 * Adds a DisplayObject instance to the end of the display list.
	 */
	public DisplayObject addChild(DisplayObject displayObject)
	{
		return this.addChildAt(displayObject, this.children.size());
	}
	
	/**
	 * Adds a DisplayObject instance to a defined position of the display list.
	 */
	public DisplayObject addChildAt(DisplayObject displayObject, int index)
	{
		if(displayObject != null)
		{
			index = (int)MathUtils.constrain(index, 0, this.children.size());
			
			if(!this.children.contains(displayObject))
			{
				if(displayObject.parent != null) displayObject.parent.removeChild(displayObject);
				displayObject.setParent(this);
				
				this.children.insertElementAt(displayObject, index);
				return displayObject;
			}
			else throw new Error("Adding failed. DisplayObject is already a child of this DisplayObjectContainer.");
		}
		 
		return null;
	}
	
	/**
	 * Removes a DisplayObject instance from the display list.
	 */
	public DisplayObject removeChild(DisplayObject displayObject)
	{
		if(displayObject != null)
		{
			if(this.children.contains(displayObject))
			{
				displayObject.setParent(null);
				
				this.children.remove(displayObject);
				return displayObject;
			}
			else throw new Error("Can't remove. DisplayObject is not child of this DisplayObjectContainer");
		}
		
		return null;
	}
	
	/**
	 * Removes the DisplayObject instance at the defined index from the display list.
	 */
	public DisplayObject removeChildAt(int index)
	{
		if(MathUtils.inRange(index, 0, this.children.size() - 1))
		{
			DisplayObject displayObject = this.children.remove(index);
			displayObject.setParent(null);
				
			return displayObject;
		}
		
		return null;
	}
	
	/* child methods */
	
	/** Defines the amount of DisplayObject instances appended to this DisplayObjectConainer. */
	public int numChildren()
	{
		return this.children.size();
	}
	
	/**
	 * Returns the child at the index of the display list.
	 * Returns null if index is out of range.
	 */
	public DisplayObject getChildAt(int index)
	{
		if(MathUtils.inRange(index, 0, this.children.size() - 1)) return this.children.get(index);
		return null;
	}
	
	/** Returns the index of an appended child or -1 if the DisplayObject instance is not child of this DisplayObjectContainer. */
	public int getChildIndex(DisplayObject displayObject)
	{
		return this.children.indexOf(displayObject);
	}
	
	/** Modifies the index of a child. */
	public void setChildIndex(DisplayObject displayObject, int index)
	{
		index = (int)MathUtils.constrain(index, 0, this.children.size() - 1);
		int currentIndex = this.children.indexOf(displayObject);
		
		if(currentIndex != -1)
		{
			if(index > currentIndex) index--;
			this.children.remove(currentIndex);
			this.children.insertElementAt(displayObject, index);
		}
	}
}
